using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;

namespace Matrix.Configuration
{
    /// <summary>
    /// 配置项仓储实现
    /// </summary>
    public class ConfigurationRepository : EfCoreRepository<IMatrixConfigurationDbContext, ConfigurationItem, Guid>, IConfigurationRepository
    {
        public ConfigurationRepository(IDbContextProvider<IMatrixConfigurationDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<ConfigurationItem?> GetByNameAsync(
            Guid? tenantId,
            string @namespace,
            string name,
            bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            return await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .Where(x => x.Namespace == @namespace)
                .Where(x => x.Key == name)
                .FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<ConfigurationItem>> GetListByNamespaceAsync(
            Guid? tenantId,
            string @namespace,
            bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            return await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .Where(x => x.Namespace == @namespace)
                .OrderBy(x => x.Key)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<ConfigurationItem>> GetListByTenantAsync(
            Guid? tenantId,
            bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            return await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .OrderBy(x => x.Namespace)
                .ThenBy(x => x.Key)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<ConfigurationItem>> GetListByKeywordAsync(
            Guid? tenantId,
            string keyword,
            bool includeDetails = true,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            return await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .Where(x => x.Key.Contains(keyword) || x.Description.Contains(keyword))
                .OrderBy(x => x.Namespace)
                .ThenBy(x => x.Key)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<bool> ExistsAsync(
            Guid? tenantId,
            string @namespace,
            string name,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            return await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .Where(x => x.Namespace == @namespace)
                .Where(x => x.Key == name)
                .AnyAsync(GetCancellationToken(cancellationToken));
        }

        public async Task DeleteAllByTenantAsync(
            Guid? tenantId,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();

            var configsToDelete = await dbContext.ConfigurationItems
                .WhereIf(tenantId.HasValue, x => x.TenantId == tenantId)
                .WhereIf(!tenantId.HasValue, x => x.TenantId == null)
                .ToListAsync(GetCancellationToken(cancellationToken));

            dbContext.ConfigurationItems.RemoveRange(configsToDelete);
            await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<int> DeleteExpiredCacheItemsAsync(
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();
            var now = DateTime.UtcNow;

            var expiredItems = await dbContext.ConfigurationCacheItems
                .Where(x => x.ExpiryTime < now)
                .ToListAsync(GetCancellationToken(cancellationToken));

            dbContext.ConfigurationCacheItems.RemoveRange(expiredItems);
            return await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
        }

        public override async Task<IQueryable<ConfigurationItem>> WithDetailsAsync()
        {
            var queryable = await GetQueryableAsync();
            return queryable;
        }
    }
}